"use client";
import { CharPinyinInfo, PinYinStatus } from "./meta";
import CharBox from "./cpts/CharBox";
import PinYinMarker from "./cpts/PinyinMarker";
import { KeyboardEvent } from "react";
import { useMemoizedFn, useMount, useSafeState, useUnmount } from "ahooks";
import { getOrDie } from "@/utils";
import { StyledMarkerBox, Wrapper } from "./styles";

interface Props {
  data: CharPinyinInfo[];
  onChange?: (pinyin: string, idx: number) => void;
  onConfirm?: (idx: number) => void;
}

const getCharacterValue = (character: CharPinyinInfo) => {
  return getOrDie(
    character.pinyin?.status === PinYinStatus.UserInput
      ? character.pinyin.userInput
      : character.pinyin!.value ||
          character.pinyin!.recommend ||
          character.pinyin!.options?.at(0),
  );
};

export default function CharPinyinMarker(props: Props) {
  const [activeIdx, setActiveIdx] = useSafeState(0);
  const [openMultiSelector, setOpenMultiSelector] = useSafeState(false);
  const [optionIdx, setOptionIdx] = useSafeState(0);

  const isTargetOk = useMemoizedFn(
    (idx: number) => (props.data[idx].pinyin?.options.length ?? 1) > 1,
  );
  const getCurCharacter = useMemoizedFn(() => {
    const character = props.data[activeIdx];
    const curPinYin = getCharacterValue(character);
    return { character, curPinYin };
  });

  const onTargetOk = useMemoizedFn((targetActiveIdx: number) => {
    const _character = props.data[targetActiveIdx];
    const _pinyin = getCharacterValue(_character);
    if (_character.pinyin) {
      setOptionIdx(_character.pinyin.options.indexOf(_pinyin));
    }
    setActiveIdx(targetActiveIdx);
  });

  const onArrowLeftClick = () => {
    let targetActiveIdx = activeIdx;

    setOpenMultiSelector(false);
    setOptionIdx(0);
    while (targetActiveIdx > 0) {
      --targetActiveIdx;
      if (isTargetOk(targetActiveIdx)) {
        break;
      }
    }
    if (isTargetOk(targetActiveIdx)) {
      onTargetOk(targetActiveIdx);
    }
  };
  const onArrowRightClick = () => {
    let targetActiveIdx = activeIdx;
    setOpenMultiSelector(false);
    setOptionIdx(0);
    while (targetActiveIdx < props.data.length - 1) {
      ++targetActiveIdx;
      if (isTargetOk(targetActiveIdx)) {
        break;
      }
    }
    if (isTargetOk(targetActiveIdx)) {
      onTargetOk(targetActiveIdx);
    }
  };

  const onArrowDownClick = () => {
    const { curPinYin, character } = getCurCharacter();
    setOpenMultiSelector(true);
    if (openMultiSelector) {
      setOptionIdx(
        Math.min(optionIdx + 1, character.pinyin!.options!.length - 1),
      );
    } else {
      const _idx =
        props.data[activeIdx].pinyin?.options.indexOf(curPinYin ?? "") ?? 0;
      setOptionIdx(Math.max(_idx, 0));
    }
  };
  const onArrowUpClick = () => {
    const { curPinYin } = getCurCharacter();
    setOpenMultiSelector(true);
    if (openMultiSelector) {
      setOptionIdx(Math.max(0, optionIdx - 1));
    } else {
      const _idx =
        props.data[activeIdx].pinyin?.options.indexOf(curPinYin ?? "") ?? 0;
      setOptionIdx(Math.max(_idx, 0));
    }
  };

  const onKeyDown = useMemoizedFn(function (e) {
    const evt: KeyboardEvent = e;
    if (
      ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(evt.code)
    ) {
      evt.stopPropagation();
      evt.preventDefault();
      if (evt.code === "ArrowLeft") {
        onArrowLeftClick();
      } else if (evt.code === "ArrowRight") {
        onArrowRightClick();
      } else if (evt.code === "ArrowDown") {
        onArrowDownClick();
      } else if (evt.code === "ArrowUp") {
        onArrowUpClick();
      }
    } else if (evt.code === "Enter") {
      setOpenMultiSelector(false);
      if (props.data[activeIdx].pinyin?.status === PinYinStatus.Auto) {
        props.onConfirm && props.onConfirm(activeIdx);
      }
      onArrowRightClick();
    }
  });

  useMount(() => {
    document.addEventListener("keydown", onKeyDown);
  });
  useUnmount(() => {
    document.removeEventListener("keydown", onKeyDown);
  });

  return (
    <Wrapper className={"char-pinyin-marker"}>
      {props.data.map((info, idx) => {
        return info.char === "\n" ? (
          <br />
        ) : (
          <StyledMarkerBox key={idx} active={+(activeIdx === idx)}>
            <PinYinMarker
              data={info.pinyin}
              openMultiSelector={activeIdx === idx && openMultiSelector}
              itemIdx={
                activeIdx === idx
                  ? optionIdx
                  : info.pinyin?.options?.indexOf(getCharacterValue(info))
              }
              onPinYinMarked={(pinyin: string) => {
                props.onChange && props.onChange(pinyin, idx);
              }}
              onClose={() => {
                setOpenMultiSelector(false);
              }}
            />
            <CharBox>{info.char}</CharBox>
          </StyledMarkerBox>
        );
      })}
    </Wrapper>
  );
}
